﻿#include "topo_mesh.h"
#include "topo_coedge.h"
#include "topo_edge.h"
#include "topo_face.h"
#include "topo_loop.h"
#include "topo_vertex.h"

#define FAST_OBJ_IMPLEMENTATION
#include "fast_obj.h"

#include <fstream>

CAGD::TopoMesh::TopoMesh(MeshType type, std::string name) : m_type(type), m_name(name)
{
    m_material.enable = false;
}

CAGD::TopoMesh::~TopoMesh()
{
    // 移除属性
    while (!m_vertexProperties.empty())
    {
        auto it = m_vertexProperties.begin();
        auto &m = it->second;
        for (auto &p : m)
            delete p.second;

        m_vertexProperties.erase(it);
    }

    while (!m_faceProperties.empty())
    {
        auto it = m_faceProperties.begin();
        auto &m = it->second;
        for (auto &p : m)
            delete p.second;

        m_faceProperties.erase(it);
    }

    while (!m_edgeProperties.empty())
    {
        auto it = m_edgeProperties.begin();
        auto &m = it->second;
        for (auto &p : m)
            delete p.second;

        m_edgeProperties.erase(it);
    }

    while (!m_coedgeProperties.empty())
    {
        auto it = m_coedgeProperties.begin();
        auto &m = it->second;
        for (auto &p : m)
            delete p.second;

        m_coedgeProperties.erase(it);
    }

    // 移除孤立顶点
    DeleteIsolated();

    // f 删除 face，co 删除 co->edge->vertex
    for (auto f : m_faces)
        delete f;

    // 注意先解除与 twin 的绑定
    for (auto co : m_coedges)
    {
        co->Twin() = nullptr;
        co->Edge() = nullptr;
        delete co;
    }
}

CAGD::TopoType CAGD::TopoMesh::Type() const
{
    return TopoType::MESH;
}

std::string CAGD::TopoMesh::Name() const
{
    return m_name;
}

int CAGD::TopoMesh::VertexNum() const
{
    return m_vertices.size();
}

int CAGD::TopoMesh::FaceNum() const
{
    return m_faces.size();
}

int CAGD::TopoMesh::CoedgeNum() const
{
    return m_coedges.size();
}

int CAGD::TopoMesh::EdgeNum() const
{
    return m_edges.size();
}

std::unordered_set<CAGD::VertexHandle> &CAGD::TopoMesh::Vertices()
{
    return m_vertices;
}

std::unordered_set<CAGD::EdgeHandle> &CAGD::TopoMesh::Edges()
{
    return m_edges;
}

std::unordered_set<CAGD::CoedgeHandle> &CAGD::TopoMesh::Coedges()
{
    return m_coedges;
}

std::unordered_set<CAGD::FaceHandle> &CAGD::TopoMesh::Faces()
{
    return m_faces;
}

void CAGD::TopoMesh::RequireFaceNormals()
{
    FPropertyHandle<BVecf> fnormalHandle("normals");

    if (m_faceProperties.find("normals") == m_faceProperties.end())
        AddProperty(fnormalHandle);

    // 计算面法向
    for (auto f : m_faces)
    {
        // 注意不要引用
        TopoCoedge *co = f->Loop()->Coedge();
        auto p0 = Origin(co)->Geom()->Point();

        co = co->Next();
        auto p1 = Origin(co)->Geom()->Point();

        co = co->Next();
        auto p2 = Origin(co)->Geom()->Point();

        Property(fnormalHandle, f) = ((p1 - p0) % (p2 - p1)).Normalized();
    }
}

void CAGD::TopoMesh::RemoveFaceNormals()
{
    FPropertyHandle<BVecf> fnormalHandle("normals");
    RemoveProperty(fnormalHandle);
}

void CAGD::TopoMesh::RequireVertexNormals()
{
    VPropertyHandle<BVecf> vnormalHandle("normals");
    FPropertyHandle<BVecf> fnormalHandle("normals");

    if (m_vertexProperties.find("normals") == m_vertexProperties.end())
        AddProperty(vnormalHandle);

    RequireFaceNormals();

    // 计算顶点法向
    for (auto v : m_vertices)
    {
        auto fList = AdjacentFaces(v);

        BVec n;
        for (auto f : fList)
            n += Property(fnormalHandle, f);

        Property(vnormalHandle, v) = n.Normalized();
    }

    RemoveFaceNormals();
}

void CAGD::TopoMesh::RemoveVertexNormals()
{
    VPropertyHandle<BVecf> vnormalHandle("normals");
    RemoveProperty(vnormalHandle);
}

void CAGD::TopoMesh::RequireVertexTexcoords()
{
    if (m_vertexProperties.find("texcoords") == m_vertexProperties.end())
    {
        VPropertyHandle<BPnt2df> vtexcoordHandle("texcoords");
        AddProperty(vtexcoordHandle);
    }
}

void CAGD::TopoMesh::RemoveVertexTexcoords()
{
    VPropertyHandle<BPnt2df> vtexcoordHandle("texcoords");
    RemoveProperty(vtexcoordHandle);
}

CAGD::VertexHandle CAGD::TopoMesh::CreateVertex(Handle(GeomPoint) p, bool testSame)
{
    // 如果开启检测，会禁止构造重合的顶点
    if (testSame)
    {
        for (auto &v : m_vertices)
        {
            // 返回重合顶点
            if (v->Geom()->Point().Distance(p->Point()) < CAGD_ABS_ACC)
                return v;
        }
    }

    VertexHandle v(new TopoVertex(p));
    m_vertices.insert(v);

    return v;
}

CAGD::CoedgeHandle CAGD::TopoMesh::CreateEdge(VertexHandle v1, VertexHandle v2, bool testSame)
{
    // 如果开启检测，会禁止构造重合的边
    if (testSame)
    {
        // 遍历 v1 周围的边，检查是否有顶点是 v2
        auto eList = v1->Edges();

        for (auto &e : eList)
        {
            if (e->Start() == v2)
                return e->Coedge()->Twin();
            if (e->End() == v2)
                return e->Coedge();
        }
    }

    // 创建新的边
    EdgeHandle e(new TopoEdge(v1, v2));
    m_edges.insert(e);

    m_coedges.insert(e->Coedge());
    m_coedges.insert(e->Coedge()->Twin());

    return e->Coedge();
}

std::vector<CAGD::FaceHandle> CAGD::TopoMesh::CreateFace(const std::vector<CoedgeHandle> &es)
{
    assert(es.size() > 2);

    // 创建面
    FaceHandle f(new TopoFace);
    m_faces.insert(f);

    // 直接创建 loop，不需要创建边
    auto loop = new TopoLoop;
    loop->Face() = f;
    loop->Coedge() = es.front();
    f->Loop() = loop;

    // 获得一个起点
    auto &e0 = es.front();
    e0->Prev() = es.back();
    es.back()->Next() = e0;
    e0->Loop() = loop;

    // 需要建立半边之间的联系
    for (int i = 1; i < es.size(); i++)
    {
        es[i]->Prev() = es[i - 1];
        es[i - 1]->Next() = es[i];
        es[i]->Loop() = loop;
    }

    return std::vector<FaceHandle>({f});
}

std::vector<CAGD::FaceHandle> CAGD::TopoMesh::CreateFace(const std::vector<VertexHandle> &vs)
{
    assert(vs.size() > 2);

    // 注意检查是否重复构造
    std::vector<CoedgeHandle> coList;
    coList.push_back(CreateEdge(vs.back(), vs.front(), true));

    for (int i = 1; i < vs.size(); i++)
        coList.push_back(CreateEdge(vs[i - 1], vs[i], true));

    return CreateFace(coList);
}

bool CAGD::TopoMesh::DeleteVertex(VertexHandle v)
{
    if (v == nullptr)
        return false;

    // 通过边来删除顶点
    auto eList = v->Edges();
    for (auto &e : eList)
        DeleteEdge(e);

    return true;
}

bool CAGD::TopoMesh::DeleteEdge(EdgeHandle e)
{
    if (e == nullptr)
        return false;

    // 先删除相邻的面
    auto co1 = e->Coedge();
    auto co2 = e->Coedge()->Twin();

    if (co1->Loop() != nullptr)
        DeleteFace(co1->Loop()->Face());

    if (co2->Loop() != nullptr)
        DeleteFace(co2->Loop()->Face());

    m_coedges.erase(co1);
    m_coedges.erase(co2);
    m_edges.erase(e);

    // 删除属性
    for (auto &m : m_coedgeProperties)
    {
        auto it1 = m.second.find(co1);
        auto it2 = m.second.find(co2);
        delete it1->second;
        delete it2->second;
        m.second.erase(it1);
        m.second.erase(it2);
    }

    for (auto &m : m_edgeProperties)
    {
        auto it = m.second.find(e);
        delete it->second;
        m.second.erase(it);
    }

    // 只需要删除 coedge，其析构函数会自动删除 edge
    // edge 析构会自动删除 vertex
    delete co1;
    delete co2;

    return true;
}

bool CAGD::TopoMesh::DeleteFace(FaceHandle f)
{
    if (f == nullptr)
        return false;

    // 只需要移除所有 face 相关的属性
    m_faces.erase(f);
    for (auto &m : m_faceProperties)
    {
        auto it = m.second.find(f);
        delete it->second;
        m.second.erase(it);
    }
    delete f;

    return true;
}

bool CAGD::TopoMesh::DeleteIsolated()
{
    for (auto it = m_vertices.begin(); it != m_vertices.end();)
    {
        if ((*it)->Edges().empty())
        {
            delete *it;
            it = m_vertices.erase(it);
        }
        else
            ++it;
    }
    return false;
}

CAGD::VertexHandle CAGD::TopoMesh::Origin(CoedgeHandle co) const
{
    VertexHandle v = nullptr;
    if (co->Edge()->Coedge() == co)
        v = co->Edge()->Start();
    else
        v = co->Edge()->End();
    return v;
}

CAGD::VertexHandle CAGD::TopoMesh::Destination(CoedgeHandle co) const
{
    VertexHandle v = nullptr;
    if (co->Edge()->Coedge() == co)
        v = co->Edge()->End();
    else
        v = co->Edge()->Start();
    return v;
}

std::vector<CAGD::VertexHandle> CAGD::TopoMesh::Vertices(FaceHandle f) const
{
    std::vector<VertexHandle> vList;

    // 取第一个 loop 上的所有顶点
    auto loop = f->Loop();
    CoedgeHandle co = loop->Coedge();
    vList.push_back(Origin(co));

    while (co->Next() != loop->Coedge())
    {
        vList.push_back(Destination(co));
        co = co->Next();
    }
    return vList;
}

std::vector<CAGD::EdgeHandle> CAGD::TopoMesh::Edges(FaceHandle f) const
{
    std::vector<EdgeHandle> eList;

    // 取第一个 loop 上的所有顶点
    auto loop = f->Loop();
    CoedgeHandle co = loop->Coedge();
    eList.push_back(co->Edge());

    while (co->Next() != loop->Coedge())
    {
        co = co->Next();
        eList.push_back(co->Edge());
    }
    return eList;
}

std::vector<CAGD::EdgeHandle> CAGD::TopoMesh::CommonEdges(FaceHandle f0, FaceHandle f1) const
{
    std::vector<EdgeHandle> eList;

    auto loop0 = f0->Loop();
    auto loop1 = f1->Loop();

    CoedgeHandle co0 = loop0->Coedge();
    CoedgeHandle co1 = loop1->Coedge();

    do
    {
        do
        {
            // 循环记录公共边
            if (co0->Edge() == co1->Edge())
                eList.push_back(co0->Edge());

            co1 = co1->Next();
        } while (co1 != loop1->Coedge());
        co0 = co0->Next();
        co1 = loop1->Coedge();
    } while (co0 != loop0->Coedge());

    return eList;
}

std::unordered_set<CAGD::VertexHandle> CAGD::TopoMesh::AdjacentVertices(VertexHandle v) const
{
    std::unordered_set<VertexHandle> vList;

    // 首先获得连接 v 的边
    auto eList = v->Edges();
    for (auto e : eList)
    {
        if (e->Start() == v)
            vList.insert(e->End());
        else
            vList.insert(e->Start());
    }
    return vList;
}

std::unordered_set<CAGD::FaceHandle> CAGD::TopoMesh::AdjacentFaces(VertexHandle v) const
{
    // 使用 set 避免重复元素
    std::unordered_set<FaceHandle> fList;

    // 首先获得连接 v 的边
    auto eList = v->Edges();
    for (auto e : eList)
    {
        fList.insert(e->Coedge()->Loop()->Face());
        fList.insert(e->Coedge()->Twin()->Loop()->Face());
    }
    return fList;
}

std::unordered_set<CAGD::FaceHandle> CAGD::TopoMesh::AdjacentFaces(FaceHandle f) const
{
    // 使用 set 避免重复元素
    std::unordered_set<FaceHandle> fList;

    // 首先获得 f 的所有边
    auto eList = Edges(f);
    for (auto e : eList)
    {
        fList.insert(e->Coedge()->Loop()->Face());
        fList.insert(e->Coedge()->Twin()->Loop()->Face());
    }

    // 移除面本身
    auto it = std::find(fList.begin(), fList.end(), f);
    fList.erase(it);

    return fList;
}

Handle(CAGD::TriangleMesh) CAGD::TopoMesh::ToTriangleMesh()
{
    Handle(TriangleMesh) mesh(new TriangleMesh);

    // 临时添加索引标记
    VPropertyHandle<int> vindexHandle("indices");
    AddProperty(vindexHandle);

    VPropertyHandle<BVecf> vnormalHandle("normals");
    VPropertyHandle<BPnt2df> vtexcoordHandle("texcoords");

    int ind = 0;
    for (auto v : m_vertices)
    {
        Property(vindexHandle, v) = ind++;
        mesh->vertices.push_back(v->Geom()->Point());
        mesh->normals.push_back(Property(vnormalHandle, v));
        mesh->texcoords.push_back(Property(vtexcoordHandle, v));
    }

    for (auto f : m_faces)
    {
        auto vList = Vertices(f);

        // 记录三角形
        mesh->triangles.push_back(
            {Property(vindexHandle, vList[0]), Property(vindexHandle, vList[1]), Property(vindexHandle, vList[2])});
    }

    // 移除临时索引
    RemoveProperty(vindexHandle);

    return mesh;
}

Handle(CAGD::TopoMesh) CAGD::load_from_obj(const std::string &filename, float scale)
{
    fastObjMesh *m = fast_obj_read(filename.c_str());
    assert(m != nullptr);

    Handle(TopoMesh) mesh(new TopoMesh(MeshType::TRI, filename));

    // 添加属性
    VPropertyHandle<BVecf> vnormalHandle("normals");
    VPropertyHandle<BPnt2df> vtexcoordHandle("texcoords");

    mesh->AddProperty(vnormalHandle);
    mesh->AddProperty(vtexcoordHandle);

    // 只保存一个材质
    // if (m->material_count)
    //{
    //    mesh->m_material.enable = true;
    //    mesh->m_material.diffuse = {m->materials[0].Kd[0], m->materials[0].Kd[1], m->materials[0].Kd[2]};
    //    mesh->m_material.specular = {m->materials[0].Ks[0], m->materials[0].Ks[1], m->materials[0].Ks[2]};
    //}

    // TIMER_START(Read)

    // 预先创建顶点，第一个顶点没有用，但是在后面作为点的存在性判断，需要空出来
    std::vector<VertexHandle> vhandles({nullptr});
    for (unsigned int i = 1; i < m->position_count; i++)
    {
        Handle(GeomPoint) point(new GeomPoint(
            {m->positions[3 * i + 0] * scale, m->positions[3 * i + 1] * scale, m->positions[3 * i + 2] * scale}));
        auto vhandle = mesh->CreateVertex(point);
        vhandles.push_back(vhandle);
    }

    // 创建面
    for (unsigned int ii = 0; ii < m->group_count; ii++)
    {
        // 获得组
        const fastObjGroup &grp = m->groups[ii];

        // 记录名称
        // std::string grp_name;
        // if (grp.name)
        //    grp_name = std::string(grp.name);

        int idx = 0;
        for (unsigned int jj = 0; jj < grp.face_count; jj++)
        {
            unsigned int fv = m->face_vertices[grp.face_offset + jj];

            std::vector<VertexHandle> fvs;
            for (unsigned int kk = 0; kk < fv; kk++)
            {
                fastObjIndex &mi = m->indices[grp.index_offset + idx];

                // 点坐标，根据 if 条件，mi.p > 0，因此第一个点用不上
                if (mi.p)
                {
                    auto vh = vhandles[mi.p];
                    fvs.push_back(vh);

                    if (mi.t)
                        mesh->Property(vtexcoordHandle, vh) = {m->texcoords[2 * mi.t + 0], m->texcoords[2 * mi.t + 1]};

                    if (mi.n)
                        mesh->Property(vnormalHandle, vh) = {m->normals[3 * mi.n + 0], m->normals[3 * mi.n + 1],
                                                             m->normals[3 * mi.n + 2]};

                    idx++;
                }
            }

            // 创建面
            mesh->CreateFace(fvs);
        }
    }

    // 如果没有法向，就自动计算法向
    if (!m->normal_count)
        mesh->RequireVertexNormals();

    // TIMER_QT_END(Read)

    fast_obj_destroy(m);
    return mesh;
}

Handle(CAGD::TopoMesh) CAGD::load_from_dat(const std::string &filename, float scale)
{
    // 文件打不开报错
    std::fstream fp(filename, std::ios::in);
    assert(fp.is_open());

    // 网格名就是文件名
    Handle(TopoMesh) mesh(new TopoMesh(MeshType::TRI, filename));

    // TIMER_START(Read)

    std::string detail;
    double value;

    // 首先跳过各个属性的描述
    fp >> detail >> value;
    fp >> detail >> value;

    // 读取材质信息
    fp >> detail >> value;
    double M[2][3];
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 3; j++)
            fp >> M[i][j];

    mesh->m_material.diffuse.SetX(M[0][0]);
    mesh->m_material.diffuse.SetY(M[0][1]);
    mesh->m_material.diffuse.SetZ(M[0][2]);
    mesh->m_material.specular.SetX(M[1][0]);
    mesh->m_material.specular.SetY(M[1][1]);
    mesh->m_material.specular.SetZ(M[1][2]);

    // 暂存顶点，用于重合顶点的情形
    std::vector<VertexHandle> vList;

    // 然后根据第一个字符判断读取内容
    fp >> detail;
    while (detail == "v")
    {
        double x, y, z;
        fp >> x >> y >> z;

        Handle(GeomPoint) p(new GeomPoint({x, y, z}));
        vList.push_back(mesh->CreateVertex(p));
        fp >> detail;
    }

    // 长度为 6 就足够了
    int k = 0;
    while (detail == "f")
    {
        int i, n;
        fp >> n;

        // 禁止更高的边数
        if (n > 6)
            return nullptr;

        // 当开启检测时，使用记录列表中的顶点
        std::vector<VertexHandle> v;
        for (int j = 0; j < n; j++)
        {
            fp >> i;
            v.push_back(vList[i]);
        }

        // 直接创建面
        mesh->CreateFace(v);
        fp >> detail;
    }

    VPropertyHandle<BVecf> vnormalHandle("normals");
    mesh->AddProperty(vnormalHandle);

    int i = 0;
    while (detail == "n")
    {
        double x, y, z;
        fp >> x >> y >> z;

        mesh->Property(vnormalHandle, vList[i++]) = BVec{x, y, z};
        fp >> detail;
    }

    VPropertyHandle<BPnt2df> vtexcoordHandle("texcoords");
    mesh->AddProperty(vtexcoordHandle);

    i = 0;
    while (detail == "t")
    {
        double x, y;
        fp >> x >> y;

        mesh->Property(vtexcoordHandle, vList[i++]) = BPnt2d{x, y};
        fp >> detail;
    }

    // TIMER_QT_END(Read)

    return mesh;
}

void CAGD::save_as_dat(Handle(TopoMesh) mesh, const std::string &filename)
{
    // TIMER_START(Write)

    // 首先输出各个属性的描述
    std::fstream fp(filename, std::ios::out);
    fp << "VertexNum " << mesh->VertexNum() << std::endl;
    fp << "FaceNum " << mesh->FaceNum() << std::endl;

    // 输出材质信息
    fp << "Material 1.0 ";
    fp << mesh->m_material.diffuse.X() << " " << mesh->m_material.diffuse.Y() << " " << mesh->m_material.diffuse.Z()
       << " ";
    fp << mesh->m_material.specular.X() << " " << mesh->m_material.specular.Y() << " " << mesh->m_material.specular.Z()
       << " ";
    fp << std::endl;

    // 临时添加索引标记
    VPropertyHandle<int> vindexHandle("indices");
    FPropertyHandle<int> findexHandle("indices");
    mesh->AddProperty(vindexHandle);
    mesh->AddProperty(findexHandle);

    int ind = 0;
    for (auto v : mesh->Vertices())
    {
        mesh->Property(vindexHandle, v) = ind++;
        fp << "v " << v->Geom()->Point().X() << " " << v->Geom()->Point().Y() << " " << v->Geom()->Point().Z()
           << std::endl;
    }

    // 输出面信息
    ind = 0;
    for (auto f : mesh->Faces())
    {
        auto vList = mesh->Vertices(f);

        // 先输出顶点数，然后输出顶点的索引
        fp << "f " << vList.size() << " ";
        for (int j = 0; j < vList.size(); j++)
            fp << mesh->Property(vindexHandle, vList[j]) << " ";
        fp << std::endl;

        // 对面编号
        mesh->Property(findexHandle, f) = ind++;
    }

    // 如果有顶点法向就输出顶点法向
    VPropertyHandle<BVecf> vnormalHandle("normals");
    if (!mesh->HasProperty(vnormalHandle))
        mesh->RequireVertexNormals();

    for (auto v : mesh->Vertices())
    {
        auto &p = mesh->Property(vnormalHandle, v);
        fp << "n " << p.X() << " " << p.Y() << " " << p.Z() << std::endl;
    }

    // 如果有纹理坐标就输出
    VPropertyHandle<BPnt2df> vtexcoordHandle("texcoords");
    if (!mesh->HasProperty(vtexcoordHandle))
        mesh->RequireVertexTexcoords();

    for (auto v : mesh->Vertices())
    {
        auto &p = mesh->Property(vtexcoordHandle, v);
        fp << "t " << p.X() << " " << p.Y() << std::endl;
    }

    // 移除属性
    mesh->RemoveProperty(vindexHandle);
    mesh->RemoveProperty(findexHandle);

    // 最后输出终止字符
    fp << "finish" << std::endl;

    // TIMER_QT_END(Write)
}
